/*
 * Copyright 2008 Zoundry LLC, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.zoundry.gaedt.core.models;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import org.apache.commons.lang.WordUtils;

/**
 * A base implementation of a model factory.  This factory can be configured with a
 * class prefix and a map of namespace to java package.  These two pieces of information
 * are used to construct the fully qualified name of a Java class to use when creating
 * a new instance of a model.
 */
public class ZGaeModelFactory implements IZGaeModelFactory
{
    /** An optional class prefix for all created models. */
    private String mPrefix;
    /** An optional class suffix for all created models. */
    private String mSuffix;
    /** Map of xml namespace to Java package. */
    private Map<String, String> mNamespaceToPackageMap;
    /** The classloader to use when loading model classes. */
    private ClassLoader mModelClassLoader;

    /**
     * C'tor.
     */
    public ZGaeModelFactory()
    {
        this("", "");
    }

    /**
     * C'tor.
     *
     * @param aPrefix
     * @param aSuffix
     */
    public ZGaeModelFactory(String aPrefix, String aSuffix)
    {
        setPrefix(aPrefix);
        setSuffix(aSuffix);
        setNamespaceToPackageMap(new HashMap<String, String>());
    }

    /**
     * @see org.zoundry.gaedt.core.models.IZGaeModelFactory#createModel(javax.xml.namespace.QName, org.zoundry.gaedt.core.models.ZGaeModel)
     */
    public ZGaeModel createModel(QName aElementName, ZGaeModel aParent)
    {
        String packageName = getNamespaceToPackageMap().get(aElementName.getNamespaceURI());
        if (packageName == null)
            packageName = getNamespaceToPackageMap().get("*");
        String className = MessageFormat.format("{0}.{1}{2}{3}", packageName, getPrefix(),
                WordUtils.capitalize(aElementName.getLocalPart()), getSuffix());
        try
        {
            Class<ZGaeModel> clazz = loadModelClass(className);
            return clazz.newInstance();
        }
        catch (Exception e)
        {
            throw new RuntimeException("Failed to create a model for " + aElementName, e);
        }
    }

    /**
     * Loads the given model class.
     *
     * @param aClassName
     * @throws ClassNotFoundException
     */
    protected Class<ZGaeModel> loadModelClass(String aClassName) throws ClassNotFoundException
    {
        if (getModelClassLoader() != null)
            return (Class<ZGaeModel>) getModelClassLoader().loadClass(aClassName);
        else
            return (Class<ZGaeModel>) Class.forName(aClassName);
    }

    /**
     * Adds a package mapping.
     *
     * @param aNamespace
     * @param aPackageName
     */
    public void addPackageMapping(String aNamespace, String aPackageName)
    {
        getNamespaceToPackageMap().put(aNamespace, aPackageName);
    }

    /**
     * Sets the default package to use.
     *
     * @param aPackageName
     */
    public void setDefaultPackage(String aPackageName)
    {
        getNamespaceToPackageMap().put("*", aPackageName);
    }

    /**
     * @return the prefix
     */
    protected String getPrefix()
    {
        return mPrefix;
    }

    /**
     * @param aPrefix the prefix to set
     */
    protected void setPrefix(String aPrefix)
    {
        mPrefix = aPrefix;
    }

    /**
     * @return the namespaceToPackageMap
     */
    protected Map<String, String> getNamespaceToPackageMap()
    {
        return mNamespaceToPackageMap;
    }

    /**
     * @param aNamespaceToPackageMap the namespaceToPackageMap to set
     */
    protected void setNamespaceToPackageMap(Map<String, String> aNamespaceToPackageMap)
    {
        mNamespaceToPackageMap = aNamespaceToPackageMap;
    }

    /**
     * @return the suffix
     */
    protected String getSuffix()
    {
        return mSuffix;
    }

    /**
     * @param aSuffix the suffix to set
     */
    protected void setSuffix(String aSuffix)
    {
        mSuffix = aSuffix;
    }

    /**
     * @return the modelClassLoader
     */
    protected ClassLoader getModelClassLoader()
    {
        return mModelClassLoader;
    }

    /**
     * @param aModelClassLoader the modelClassLoader to set
     */
    public void setModelClassLoader(ClassLoader aModelClassLoader)
    {
        mModelClassLoader = aModelClassLoader;
    }
}
